# -*- coding: utf-8 -*-
# Gestión de ingeniería inversa de la aplicación.

## vipera, un diseñador de aplicaciones para Python
##     Copyright (C) 2011 Ángel Luis García García
##
##    This program is free software: you can redistribute it and/or modify
##    it under the terms of the GNU General Public License as published by
##    the Free Software Foundation, either version 3 of the License, or
##    (at your option) any later version.
##
##    This program is distributed in the hope that it will be useful,
##    but WITHOUT ANY WARRANTY; without even the implied warranty of
##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##    GNU General Public License for more details.
##
##    You should have received a copy of the GNU General Public License
##    along with this program.  If not, see <http://www.gnu.org/licenses/>.

import wx
import wx.lib.agw.pybusyinfo as PBI
import wx.lib.ogl as ogl
import wx.lib.scrolledpanel as scrolled

import os
import pyclbr  
import StringIO
import tokenize
import sys
import codecs

from clases_design_ogl_vipera import panel_design as ogl_designer
from clases_design_ogl_vipera import MyEvtHandler, DividedShape
from ingenieria_inversa_vista import panel_ing_inversa
from editor2_vipera import panel_codigo_ed 
from configuracion_vipera import proy_por_defecto
from i18n_vipera import t

# ###############################
# Funciones abrir/salvar ficheros 
# con codificación utf-8
# ###############################

def f_open(fichero):
    '''Función para abrir fichero en utf-8'''
    # f = open(fichero,'r')
    f = codecs.open(fichero,'r','utf-8')
    ret = f.read()
    f.close()
    return ret

def f_save(fichero, datos):
    '''Función para crear y salvar fichero en utf-8'''    
    f = codecs.open(fichero,'w','utf-8')
    f.write(datos)
    f.close()

# ############################################
# Funciones para pedir abrir ó salvar fichero.
# ############################################

def f_salvar_fichero(nombre_fichero, idioma = None):
    '''Función de petición para salvar fichero'''
    # Salvamos fichero...
    ret = False
    wildcard = t(u"Fichero Python", idioma)  + " (*.py)|*.py"   
    msj = t(u"Salvar fichero como...", idioma)
    dlg = wx.FileDialog(None, message=msj, 
                        defaultFile=nombre_fichero, wildcard=wildcard,
                        style=wx.SAVE)
    # Mostramos el diálogo.
    if dlg.ShowModal() == wx.ID_OK: 
        ret = True, dlg.GetPath(), dlg.GetFilename()
    else: 
        ret = False, None, None
    # Limpiamos y nos vamos.
    dlg.Destroy()
    # Devolvemos estado.
    return ret
    
def f_abrir_fichero(idioma = None):
    '''Función para abrir/cargar fichero'''
    # Abrimos diálogo para cargar fichero Python.
    ruta, nombre_fichero = None, None
    wildcard = t("Fichero Python", idioma) + " (*.py)|*.py"   
    msj = t(u"Seleccione un fichero Python", idioma)
    dlg = wx.FileDialog(None, message=msj, 
                        defaultFile="", wildcard=wildcard,
                        style=wx.OPEN)
    # Mostramos el diálogo.
    if dlg.ShowModal() == wx.ID_OK: ruta, nombre_fichero = dlg.GetPath(), dlg.GetFilename()
    # Limpiamos y nos vamos.
    dlg.Destroy()
    return ruta, nombre_fichero

# ######
# Clases
# ######

class editor(panel_codigo_ed):
    '''Editor de código Python'''
    def __init__(self, parent, idioma = None):
        '''Constructor de clase'''
        panel_codigo_ed.__init__(self, parent, 4, False, True, True, True, idioma = idioma)
        # Idioma.
        self.l = idioma
        # Binding.
        self.panel.m_button_abrir.Bind(wx.EVT_BUTTON, self.OnAbrir)
        self.panel.m_button_nuevo.Bind(wx.EVT_BUTTON, self.OnNuevo)
        self.panel.m_button_salvar.Bind(wx.EVT_BUTTON, self.OnSalvar)
    
    def referencias(self, designer):
        '''Referencia al diseñador'''
        self.__designer = designer

    def __nuevo(self):
        '''Nuevo documento'''
        designer = self.__designer
        # Intentamos eliminar algo, si es que efectivamente había algo.
        designer.panel.panel.diagram.DeleteAllShapes()
        # except: pass
        designer.panel.panel.Refresh()
        # Borramos nombre de fichero.
        designer.ruta_completa_fichero.SetValue("")
        designer.nombre_fichero.SetValue("")
        # Borramos las funciones.
        designer.lista_funciones.DeleteAllItems()
        # Y lo que hay en el editor.
        designer.editor.panel.editor.SetText("")

    # ######################
    # Manejadores de eventos
    # ######################
        
    def OnAbrir(self, event):
        '''Abrimos diálogo para cargar fichero Python.'''
        ruta, nombre_fichero = f_abrir_fichero(idioma = self.l)
        if ruta is None: return -1
        self.__nuevo()
        designer = self.__designer
        texto = f_open(ruta)
        designer.editor.panel.editor.SetText(texto)
        designer.ruta_completa_fichero.SetValue(ruta)
        designer.nombre_fichero.SetValue(nombre_fichero)

    def OnNuevo(self, event):
        '''Módulo nuevo'''
        msj = t(u"¿Crear un módulo nuevo?", self.l)
        i = wx.MessageBox(msj, t(u"Atención", self.l), wx.YES_NO)
        if i != wx.YES: return
        self.__nuevo()
        
    def OnSalvar(self, event):
        '''Salvar módulo'''
        designer = self.__designer
        nombre_fichero = designer.ruta_completa_fichero.GetValue()
        if len(nombre_fichero.strip()) == 0: 
            nombre_fichero = t('sin_nombre.py', self.l)
        seguir, path, nfich = f_salvar_fichero(nombre_fichero, self.l)
        if seguir:
            # Actualizamos datos.
            designer.ruta_completa_fichero.SetValue(path)
            designer.nombre_fichero.SetValue(nfich)
            texto = designer.editor.panel.editor.GetText()
            f_save(path, texto)

class panel_analisis(wx.Panel):
    '''Panel que contiene el canvas gráfico'''
    def __init__(self, parent):
        '''Constructor de clase'''
        wx.Panel.__init__(self, parent)
        self.panel = ogl_designer(self)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.panel, 1,wx.ALL|wx.EXPAND, 5)
        self.SetSizer(sizer)
              
class panel_designer_class(wx.Panel):
    '''Panel que contiene el diseñador gráfico de diagramas de clases'''
    def __init__(self, parent, editor, ruta_completa_fichero, 
                 nombre_fichero, lista_funciones, tope_ogl = None, idioma = None):
        '''Constructor de clase'''
        wx.Panel.__init__(self, parent)
        # Idioma.
        self.l = idioma
        self.panel = panel_analisis(self)
        self.editor = editor
        self.ruta_completa_fichero = ruta_completa_fichero
        self.lista_funciones = lista_funciones
        self.__tope = tope_ogl
        self.nombre_fichero = nombre_fichero
        self.boton_cargar = wx.BitmapButton(self, -1, 
                                            wx.Bitmap(u"recursos/images/open_folder.png", wx.BITMAP_TYPE_ANY), 
                                            wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW)
        self.boton_cargar.SetToolTipString(t(u"Abrir fichero Python", self.l))
        self.boton_recargar = wx.BitmapButton(self, -1, 
                                              wx.Bitmap(u"recursos/images/reload_icon.jpg", wx.BITMAP_TYPE_ANY), 
                                              wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW)
        self.boton_recargar.SetToolTipString(t(u"Volver a abrir el fichero Python actual", self.l))
        # Sizer.
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer2 = wx.BoxSizer(wx.HORIZONTAL)
        sizer2.Add(self.boton_cargar, 0,wx.ALL, 1)
        sizer2.Add(self.boton_recargar, 0,wx.ALL, 1)        
        sizer.Add(sizer2, 0, wx.ALL, 1)
        sizer.Add(self.panel, 1,wx.ALL|wx.EXPAND, 1)
        self.SetSizer(sizer)
        # Binding.
        self.boton_cargar.Bind(wx.EVT_BUTTON, self.OnCargarFichero)
        self.boton_recargar.Bind(wx.EVT_BUTTON, self.OnRecargarFichero)
        # Canvas.
        self.panel.panel.crear_diagrama()

    def __clases_instanciadas_en_codigo(self, i, nombre_clase, bolsa_clases, bolsa_clases_inst):
        '''Detección de clases que se instancian en código, no propiedades o atributos'''
        if len(i.split('=')) != 1: 
            aux = i.split('=')[1].strip()
            # ¿Es una instanciación de clase?.
            aux = aux.split('(')
            if len(aux) > 1: 
                clase_inst = aux[0].strip()
                # Buscamos si efectivamente lo es...
                for j in bolsa_clases:
                    if j == clase_inst:
                        if bolsa_clases_inst.count((nombre_clase, clase_inst)) == 0:
                            bolsa_clases_inst.append((nombre_clase, clase_inst))
                            break    
                        
    def parser_modulo(self, ruta, nombre_fichero):
        '''Método que devuelve clases de un módulo dado'''
        # Incluimos el fichero en el sys.path para que funcione el analizador.
        ruta_sin_fich = ruta[0:len(ruta) - len(nombre_fichero)]
        try:
            sys.path.remove(ruta_sin_fich)
        except: pass
        sys.path.insert(0, ruta_sin_fich)
    
        # Algunas constantes.
        enter = '\n'
        tabulador = '\t'
        blanco = ' '
    
        # Módulo a analizar.
        modulo = nombre_fichero[0:len(nombre_fichero)-3]
        # Nombre completo del fichero.
        nfich = ruta
        # Recogemos el código Python.
        cadena_f = f_open(nfich)
        # Lista para transformar el código Python.
        cadena = list()
    
        # Quitamos las líneas en blanco.
        cadena_aux = StringIO.StringIO(cadena_f)
        cadena_f = ""    
        for i in cadena_aux:
            if len(i.strip()) != 0: 
                cadena_f += i
                cadena.append(i)
        
        # Creamos buffer de cadena.
        texto = StringIO.StringIO(cadena_f)
        
        # Creamos tokens.
        tokens = tokenize.generate_tokens(texto.readline)
        
        for a,b,c,d,_ in tokens:
            if a == tokenize.STRING:
                if "property" in b or "self." in b:
                    # Buscamos cadenas de texto.
                    cadenas = list()
                    if c[0] <> d[0]:
                        cadenas.append((c,(c[0],-1)))
                        for j in range(c[0] + 1, d[0]):
                            cadenas.append(((j,0),(j,-1)))
                        cadenas.append(((d[0],0),d))
                    else: cadenas.append((c,d))
                    # Cambiamos los items por blancos.
                    for k in cadenas:
                        linea = k[0][0]
                        aux = cadena[linea - 1]
                        aux = aux.replace('property',blanco)
                        aux = aux.replace('self.',blanco)
                        cadena[linea - 1] = aux
            if a == tokenize.COMMENT:
                if "property" in b or "self." in b:
                    cadena[c[0] - 1] = cadena[c[0] - 1].split('#')[0]
        
        # Liberamos memoria.            
        tokens.close()
        
        # Bolsa de funciones.
        bolsa_funciones = list()
        # Bolsa de clases instanciadas en atributos.
        bolsa_clases_inst = list()
        # Definimos bolsa de propiedades.
        bolsa_propiedades = list()
        # Definimos bolsa de clases.
        bolsa_clases = list()
        # Definimos bolsa para clases padre.
        bolsa_clases_padre = list()
        # Definimos bolsa para métodos de clase.
        bolsa_metodos = list()
        # Bolsa de atributos.
        bolsa_atributos = list()
        # Analizamos módulo.
        reload(pyclbr)
        ret = pyclbr.readmodule_ex(modulo)
        for nombre, objeto in ret.items():
            # Es una función.
            if isinstance(objeto, pyclbr.Function):
                # Añadimos función.
                bolsa_funciones.append(nombre)
            # Es una clase.
            if isinstance(objeto, pyclbr.Class):
                # Añadimos clase.
                bolsa_clases.append(nombre)
                # Clases de las que hereda.
                lista_clases_padre = objeto.super
                for i in lista_clases_padre: 
                    if isinstance(i, pyclbr.Class): 
                        bolsa_clases_padre.append((nombre, i.name))
                    else: 
                        bolsa_clases_padre.append((nombre, i))
                # Diccionario de métodos.
                dicc_metodos = objeto.methods
                # Incluimos método.
                for metodo, _ in dicc_metodos.items():
                    bolsa_metodos.append((nombre, metodo))
                # Número de línea de la clase que está en el módulo.
                num_linea = objeto.lineno
        
        # Bolsa auxiliar de atributos y métodos.
        bolsa = list()
        # Testigo de recorrido.
        candidato = False
        # Recorremos las líneas del fichero.
        for i in cadena:
            # ¿Estamos dentro de una clase?
            if candidato and (i[0] == blanco or i[0] == tabulador):
                # Atributos.
                if 'self.' in i: 
                    # Buscamos primero clases instanciadas en código, no en atributos.
                    self.__clases_instanciadas_en_codigo(i, nombre_clase, bolsa_clases, bolsa_clases_inst)
                    # Ahora los atributos...
                    atrib = i.split('self.')[1].strip().split('=')[0].strip().split('(')[0].strip()
                    # [28-09-2011] Si hay caracteres especiales, seguimos.
                    if atrib.count('.') != 0 or \
                       atrib.count(',') != 0 or \
                       atrib.count(':') != 0 or \
                       atrib.count('!') != 0 or \
                       atrib.count('}') != 0 or \
                       atrib.count(']') != 0 or \
                       atrib.count(')') != 0 : continue
                    if bolsa.count((nombre_clase, atrib)) == 0: bolsa.append((nombre_clase, atrib))
                    # Buscamos clases que se instancian en el atributo.
                    aux = i.split('self.')[1].strip()# .split('=')[1].strip()
                    # ¿Hay asignación?.
                    aux = aux.split('=')
                    if len(aux) > 1: 
                        # ¿Es una instanciación de clase?.
                        aux = aux[1].split('(')
                        if len(aux) > 1: 
                            clase_inst = aux[0].strip()
                            # Buscamos si efectivamente lo es...
                            for j in bolsa_clases:
                                if j == clase_inst:
                                    bolsa_clases_inst.append((nombre_clase, clase_inst))
                                    break
                # Propiedades.
                if 'property' in i:
                    prop = i.split('property')[0].split('=')[0].strip()
                    # aux = '@%s' % nombre_clase 
                    bolsa_propiedades.append((nombre_clase, prop))
                # [29-11-2011] Clases que se instancian en el código (no atributos ni propiedades).
                self.__clases_instanciadas_en_codigo(i, nombre_clase,bolsa_clases, bolsa_clases_inst)
                # Siguiente línea.
                continue
            # ¿Hay una clase?
            if i[0:5] == 'class':
                candidato = True
                # Nombre de la clase.
                nombre_clase = i[5:].split('(')[0].split(':')[0].strip()
                # print nombre_clase
            else:
                candidato = False
        
        # Atributos.
        for i in bolsa:
            if i not in bolsa_metodos: bolsa_atributos.append(i)
        
        # Construimos el resultado de cada clase.
        clases_c = list()
        for i in bolsa_clases:
            # Métodos.
            metodos_c = list()
            for j in bolsa_metodos:
                if j[0] == i: 
                    # Si es oculto, no se incluye.
                    if j[1][0:2] == '__' and j[1] not in ['__init__','__del__']: continue
                    metodos_c.append(j[1])
            # Atributos.
            atributos_c = list()
            for j in bolsa_atributos:
                if j[0] == i: 
                    # Si es oculto, no se incluye.
                    if j[1][0:2] == '__': continue
                    atributos_c.append(j[1])
            # Propiedades.
            propiedades_c = list()
            for j in bolsa_propiedades:
                if j[0] == i: 
                    # Incluimos una arroba para identificar la propiedad.
                    aux = '@%s' % j[1] 
                    propiedades_c.append(aux)
            # Clases padre.
            clases_padre_c = list()
            for j in bolsa_clases_padre:
                if j[0] == i: clases_padre_c.append(j[1])
            # Clases instanciadas en atributos.
            clases_inst_c = list()
            for j in bolsa_clases_inst:
                if j[0] == i: clases_inst_c.append(j[1])
    
            # Incluimos clase.
            clases_c.append((i, metodos_c, atributos_c, propiedades_c, clases_padre_c, clases_inst_c))
        
        return clases_c, bolsa_funciones

    def dibujar_cajas(self, datos):
        '''Método para dibujar clases y sus relaciones en OGL'''
        # Hacemos algo de limpieza...
        self.panel.panel.diagram.DeleteAllShapes()
        self.panel.panel.Refresh()        
        # Referenciamos el canvas.
        canvas = self.panel.panel.canvas
        # Valores de posición iniciales.
        valor_x = 100.0
        valor_y = 100.0
        # Estructura de referencias a los DividedShape.
        clases = list()
        # Recorremos las clases para dibujar las cajas..
        for k in datos:
            # print k[0]
            # Datos de la clase actual.
            nombre_clase = k[0] 
            metodos = k[1]
            atributos = k[2]
            propiedades = k[3] 
            # Creamos el DividedShape.
            shape = DividedShape(140, 150, canvas, nombre_clase, propiedades, atributos, metodos)
            # Guardamos la tupla (nombre_clase, referencia del dividedshape).
            clases.append((nombre_clase, shape))
            # Incluimos la referencia del dividedshape en la estructura.
            # Coordenadas.
            shape.SetX( valor_x )
            shape.SetY( valor_y )
            if self.__tope is None:
                tope = proy_por_defecto['ogl_tope_horizontal']
            else:
                tope = self.__tope
            if valor_x > tope:
                valor_x = 100.0
                valor_y += 200.0
            else:
                valor_x += 200.0
            # Posición de la caja.
            shape.SetX(valor_x)
            shape.SetY(valor_y)
            # Añadimos la caja al canvas.
            canvas.AddShape(shape)
            # Lo mostramos.
            self.panel.panel.diagram.ShowAll(1)
            # Manejador de eventos del dividedshape.
            evthandler = MyEvtHandler()
            evthandler.SetShape(shape)
            evthandler.SetPreviousHandler(shape.GetEventHandler())
            shape.SetEventHandler(evthandler)
            # Refrescamos el frame, para que salga todo.
            self.panel.panel.Refresh()
        # Recorremos las clases para dibujar las relaciones entre cajas.
        for k in datos:
            # Datos de la clase actual.
            nombre_clase = k[0] 
            clases_padre = k[4]
            clases_inst = k[5]
            # Referencias del hijo.
            nhijo = nombre_clase 
            for i in clases:
                if i[0] == nhijo: 
                    hijo = i[1]
                    break
            # Creamos relaciones de herencia en el OGL.
            for j in clases_padre:
                # Referencias del padre.
                npadre = j
                for l in clases:
                    if l[0] == npadre:
                        padre = l[1]
                        # Línea.
                        line = ogl.LineShape()
                        line.SetCanvas(canvas)
                        line.SetPen(wx.BLACK_PEN)
                        line.SetBrush(wx.BLACK_BRUSH)
                        line.AddArrow(ogl.ARROW_ARROW)
                        line.MakeLineControlPoints(2)
                        # Marcamos el sentido de la flecha.
                        hijo.AddLine(line, padre)
                        # Lo añadimos al canvas.
                        canvas.AddShape(line)
                        line.Show(True)
                        # Refrescamos.
                        self.panel.panel.Refresh()
            # Creamos relaciones de instanciación de clases en atributos.
            for j in clases_inst:
                # Referencias del padre.
                npadre = j
                for l in clases:
                    if l[0] == npadre:
                        padre = l[1]
                        # Línea.
                        line = ogl.LineShape()
                        line.SetCanvas(canvas)
                        line.SetPen(wx.LIGHT_GREY_PEN)
                        line.SetBrush(wx.LIGHT_GREY_BRUSH)
                        line.AddArrow(ogl.ATTACHMENT_MODE_BRANCHING)
                        line.MakeLineControlPoints(2)
                        # Marcamos el sentido de la flecha.
                        padre.AddLine(line, hijo)
                        # Lo añadimos al canvas.
                        canvas.AddShape(line)
                        line.Show(True)
                        # Refrescamos.
                        self.panel.panel.Refresh()

    def __cargar_fichero(self, ruta, nombre_fichero):
        '''Carga de fichero en el diseñador de clases'''
        # Información sobre la tarea.
        mensaje = t(u"Analizando código fuente del fichero Python...", self.l)
        busy = PBI.PyBusyInfo(mensaje, parent=None, title=t("Un momento...", self.l))
        wx.Yield()
        # Buscamos al parser para realizar la ingeniería inversa...
        # lista_clases, lista_funciones = self.parser_modulo(ruta, nombre_fichero)
        
        try:
            lista_clases, lista_funciones = self.parser_modulo(ruta, nombre_fichero)
        except:
            del busy
            wx.MessageBox(t(u"Error en la carga del fichero", self.l), t(u"Atención", self.l),wx.OK)
            return 
        
        # Y dibujamos las cajas en el OGL.
        self.dibujar_cajas(lista_clases)
        # Limpiamos funciones.
        self.lista_funciones.DeleteAllItems()
        # Recopilamos las funciones del módulo.
        for funcion in lista_funciones:
            n = self.lista_funciones.GetItemCount()
            self.lista_funciones.InsertStringItem(n,funcion)
        # Cargamos el fichero en el editor.
        texto = f_open(ruta)
        self.editor.panel.editor.SetText(texto)
        # Incluimos el nombre y ruta del fichero actual.
        self.ruta_completa_fichero.SetValue(ruta)
        # Incluimos el nombre del fichero actual.
        self.nombre_fichero.SetValue(nombre_fichero)
        # Quitamos el mensaje.
        del busy
        
    def __aviso(self, msj0):
        '''Mensaje de aviso'''
        msj1 = t(u'(Se perderán los datos que no haya guardado)', self.l)
        msj = msj0 + '\n' + msj1
        i = wx.MessageBox(msj, t(u"Atención", self.l), wx.YES_NO)
        if i != wx.YES: return False
        return True
    
    def OnCargarFichero(self, event):
        '''Carga de fichero en el diseñador de clases'''
        # Abrimos diálogo para cargar fichero Python.
        if not self.__aviso(t(u'¿Abrir un fichero Python?', self.l)): return
        ruta, nombre_fichero = f_abrir_fichero(idioma = self.l)
        if ruta is None: return -1
        self.__cargar_fichero(ruta, nombre_fichero)

    def OnRecargarFichero(self, event):
        '''Recarga de fichero en el diseñador de clases'''
        # Volvemos a recargar el fichero.
        ruta = self.ruta_completa_fichero.GetValue().strip()
        nombre_fichero = self.nombre_fichero.GetValue().strip()
        if len(ruta) == 0: return -1
        msj = t(u'¿Volver a abrir el fichero?', self.l)
        if not self.__aviso(msj): return        
        self.__cargar_fichero(ruta, nombre_fichero)
    
class panel_ing_inversa_p(panel_ing_inversa):
    '''Panel gráfico base de ingeniería inversa'''
    def __init__(self, parent, idioma = None):
        '''Constructor de clase'''
        panel_ing_inversa.__init__(self, parent)
        # Idioma.
        self.m_button_designer.SetToolTipString(t(u'Diseñador de clases', idioma))
        self.m_button_editor.SetToolTipString(t(u'Editor Python', idioma))
        self.m_choicebook.SetPageText(0, t(u'Diagrama de clases', idioma))
        self.m_choicebook.SetPageText(1, t(u'Funciones', idioma))                                      

    def OnDesigner(self, event):
        '''Manejador de eventos para mostrar el diseñador de clases'''
        self.m_splitter_principal.SetSashPosition(10000)
   
    def OnEditor(self, event):
        '''Manejador de eventos para mostrar el editor'''
        self.m_splitter_principal.SetSashPosition(10)
        
class panel_principal(scrolled.ScrolledPanel):
    '''Panel principal de ingeniería inversa'''
    def __init__(self, parent, datos_conf, idioma = None):
        '''Constructor de clase'''
        scrolled.ScrolledPanel.__init__(self, parent)
        # Idioma.
        self.l = idioma
        # Panel principal que contendrá todo.
        panel = panel_ing_inversa_p(self, self.l)
        # Configuramos el panel de funciones.
        panel.m_listCtrl_funciones.InsertColumn(0,t(u"Funciones del módulo", self.l))
        panel.m_listCtrl_funciones.SetColumnWidth(0, 1000)
        # Incluimos el editor.
        self.panel_editor = editor(panel.m_panel_editor, idioma = self.l)
        # Incluimos el diseñador (y le pasamos la referencia del 
        # editor,  la de la caja de texto con el nombre del fichero actual,
        # y la lista de funciones).
        self.panel_designer = panel_designer_class(panel.m_panel_clases, 
                                                   self.panel_editor, 
                                                   panel.m_textCtrl_fichero, 
                                                   panel.m_textCtrl_nombre_fichero,
                                                   panel.m_listCtrl_funciones,
                                                   datos_conf['tope_hor_ogl'], idioma = self.l)
        # Configuramos canvas.
        self.panel_designer.panel.panel.configurar_canvas(datos_conf['alto_ogl'],
                                                          datos_conf['ancho_ogl'],
                                                          datos_conf['color_back_ogl'])
        # Incluimos referencias al diseñador.
        self.panel_editor.referencias(self.panel_designer)
        # Y los sizers correspondientes.
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.panel_designer, 1, wx.ALL|wx.EXPAND, 5)
        panel.m_panel_clases.SetSizer(sizer)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.panel_editor, 1, wx.ALL|wx.EXPAND, 5)
        panel.m_panel_editor.SetSizer(sizer)
        # Sizer principal.
        sizer_p = wx.BoxSizer(wx.VERTICAL)
        sizer_p.Add(panel, 1, wx.ALL|wx.EXPAND, 5)
        self.SetSizer(sizer_p)
        # ¡Le damos caña al ScrolledPanel!
        self.SetupScrolling()
        